package cn.itsource.langxun.service.impl;

import cn.itsource.langxun.client.PageClient;
import cn.itsource.langxun.client.RedisClient;
import cn.itsource.langxun.domain.ProductType;
import cn.itsource.langxun.mapper.ProductTypeMapper;
import cn.itsource.langxun.service.IProductTypeService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 商品目录 服务实现类
 * </p>
 *
 * @author lishijie
 * @since 2019-08-21
 */
@Service

@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class ProductTypeServiceImpl extends ServiceImpl<ProductTypeMapper, ProductType> implements IProductTypeService {


    /**
     * <p>
     * 商品目录 服务实现类
     *
     * 项目中遇到的问题：
     * 加载所有类型的时候，所有的子类型要放到父类型的children属性中，
     * 而且类型的层次不能确定，所以我们刚开始选择递归进行查询，但是，由于
     * 每次递归都要查询数据库，导致查询效率很低，如果递归的层次比较深，可能
     * 还会导致栈溢出，所以我们对代码进行了优化。
     *
     * 一次性查询出所有的数据，在后端使用Java进行数据的封装
     * 使用一个Map存放所有的类型
     * 遍历的时候直接可以根据子类型的pid在map找到对应的父类型，
     * 把子类型添加到父类型的children集合中
     *
     *
     * </p>
     *
     */
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private PageClient pageClient;

    /*
     * 当对类型进行增删改操作的时候
     * （1）同步redis缓存
     * （2）重新生成首页面
     * */
    @Override
    @Transactional
    public boolean updateById(ProductType entity) {
        super.updateById(entity);
        synchronizedOperate();
        return true;
    }

    @Override
    @Transactional
    public boolean removeById(Serializable id) {
        super.removeById(id);
        synchronizedOperate();
        return true;
    }

    @Override
    @Transactional
    public boolean save(ProductType entity) {
        super.save(entity);
        synchronizedOperate();
        return true;
    }

    /**
     * 增删改时的同步操作
     */
    private void synchronizedOperate(){
        //更新redis缓存
        List<ProductType> productTypes = loadTreeFor_2();
        String jsonString = JSON.toJSONString(productTypes);
        redisClient.set("productTypes", jsonString);
        //重新生成首页面
        genHomePage();
    }

    /**
     * 静态化首页面
     */
    @Override
    public void genHomePage() {
        //第一步，根据product.type.vm生成product.type.vm.html
        String tempalatePath = "F:\\Programme\\shoftware\\Git\\langxun-parent\\langxun-product\\product-service\\src\\main\\resources\\template\\home\\product.type.vm";
        String targetPath = "F:\\Programme\\shoftware\\Git\\langxun-parent\\langxun-product\\product-service\\src\\main\\resources\\template\\product.type.vm.html";
        List<ProductType> productTypes = loadTree();
        pageClient.genStaticPage(tempalatePath, targetPath, productTypes);

        //第二步，再根据home.vm生成home.html
        tempalatePath = "F:\\Programme\\shoftware\\Git\\langxun-parent\\langxun-product\\product-service\\src\\main\\resources\\template\\home.vm";
        targetPath = "F:\\Programme\\shoftware\\Git\\langxun-web-parent\\ecommerce\\home.html";
        Map<String,Object> data = new HashMap<>();
        data.put("staticRoot", "F:\\Programme\\shoftware\\Git\\langxun-parent\\langxun-product\\product-service\\src\\main\\resources\\");
        pageClient.genStaticPage(tempalatePath, targetPath, data);
    }
    @Override
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    public List<ProductType> loadTree() {
        //查询redis
        String  productTypesStr = redisClient.get("productTypes");
        List<ProductType> list = null;
        //判断redis中是否有
        //问题：如果说单线程，木得问题，高并发下，第一次可能会导致多个请求穿透，同时访问数据库
        //还有其他的redis的穿透问题，id  -1000  10万次
        //(1)获取数据加锁  --  效率问题（一定不要在方法上加synchronized）
        //(2)数据库中没有查询到 key-value(null) 存储到redis中并且设置一个过期时间
        if(StringUtils.isNotEmpty(productTypesStr)){
            System.out.println("从Redis中获取值......");
            list = JSON.parseArray(productTypesStr,ProductType.class);
        }else{
            System.out.println("从数据库中获取值......");
            //查询数据库
            list = loadTreeFor_2();
            //将list转成json字符串
            String jsonStr = JSON.toJSONString(list);
            //添加到redis中
            redisClient.set("productTypes",jsonStr);
        }
        return list;
    }



    /**
     * 根据父类型的id查询所有子类型 -- 递归实现
     * @param pid
     * @return
     */
    public List<ProductType> loadTreeRecursive(Long pid){
        List<ProductType> productTypes = baseMapper.selectList(new QueryWrapper<ProductType>()
                .eq("pid", pid));
        if(productTypes==null||productTypes.size()==0){
            return null;
        }
        for (ProductType productType : productTypes) {
            List<ProductType> children = loadTreeRecursive(productType.getId());
            productType.setChildren(children);
        }
        return productTypes;
    }

    /**
     * 循环实现 -- 方式1
     * @return
     */
    public List<ProductType> loadTreeFor_1(){
        List<ProductType> productTypes = baseMapper.selectList(null);
        //新建一个List存放一级类型
        List<ProductType> fisrtLevelTypes = new ArrayList<>();

        for (ProductType productType : productTypes) {
            if(productType.getPid()==0){
                //如果是一级类型，则直接添加到fisrtLevelTypes
                fisrtLevelTypes.add(productType);
            }else{
                //如果不是一级类型，则添加到父类型的children属性中
                for (ProductType type : productTypes) {
                    if(productType.getPid()==type.getId()){
                        type.getChildren().add(productType);
                    }
                }
            }
        }

        return fisrtLevelTypes;
    }

    /**
     * 循环实现 -- 方式2
     * @return
     */
    public List<ProductType> loadTreeFor_2(){
        List<ProductType> productTypes = baseMapper.selectList(null);
        //新建一个List存放一级类型
        List<ProductType> fisrtLevelTypes = new ArrayList<>();

        //创建一个Map，存放所有的类型
        Map<Long,ProductType> map = new HashMap<>();
        for (ProductType productType : productTypes) {
            map.put(productType.getId(), productType);
        }

        //整理数据
        for (ProductType productType : productTypes) {
            if(productType.getPid()==0){
                fisrtLevelTypes.add(productType);
            }else{
                ProductType parentType = map.get(productType.getPid());
                //获取父的Children
                List<ProductType> children = parentType.getChildren();
                if(children==null){
                    children = new ArrayList<>();
                }
                //向children中添加子类型
                children.add(productType);
                //将children设置给parent
                parentType.setChildren(children);
            }
        }

        return fisrtLevelTypes;
    }


}
